package com.puzzlebazar.client.main.control;

import com.google.gwt.user.client.Window;
import com.puzzlebazar.client.main.view.MainView;
import com.puzzlebazar.client.puzzle.control.PuzzleController;
import com.puzzlebazar.client.puzzle.control.PuzzleController.PuzzleCallback;
import com.puzzlebazar.client.puzzle.model.Move;
import com.puzzlebazar.client.puzzle.model.MoveTimer;
import com.puzzlebazar.client.puzzle.model.History;
import com.puzzlebazar.client.puzzle.model.MoveTimerEmpty;
import com.puzzlebazar.client.puzzle.model.PuzzleFactory;
import com.puzzlebazar.client.puzzle.model.PuzzleModel;
import com.puzzlebazar.client.puzzle.model.MoveTimerRealtime;
import com.puzzlebazar.client.puzzle.view.ExtPuzzleView;
import com.puzzlebazar.client.squaregrid.view.SGViewImpl;
import com.puzzlebazar.client.user.UserModel;
import com.puzzlebazar.client.util.ConnectionLock;
import com.puzzlebazar.client.util.Executable;
import com.puzzlebazar.client.util.Observable;
import com.puzzlebazar.client.util.Observer;

public class EditPuzzleState extends MainState implements Observer {

	public enum Type {
		PLAY_PUZZLE,
		CONTINUE_PUZZLE,
		VIEW_SOLUTION,
		EDIT_PUZZLE, 
		VIEW_TUTORIAL,
		EDIT_TUTORIAL 
	};
	
	
	private final MyPuzzleCallback callback = new MyPuzzleCallback();

	private ExtPuzzleView extPuzzleView;

	private PuzzleModel puzzleModel = null;
	private History puzzleHistory = null;
	private SGViewImpl puzzleView = null;
	private PuzzleController puzzleController = null;
	
	private Type type = Type.PLAY_PUZZLE;

	private MoveTimer moveTimer;

	
	public EditPuzzleState( UserModel model, MainView view, MainController controller ) {
		super( model, view, controller );
	}

	@Override
	public void update(Observable observable, Object arg) {
		if( type == Type.VIEW_SOLUTION || type == Type.VIEW_TUTORIAL )
			return;
		
		if( type == Type.EDIT_PUZZLE ) {
			assert( observable == puzzleModel );
			if( puzzleModel.isSaved() )
				controller.setConfirmStateChange( null );
			else
				controller.setConfirmStateChange( "Changes have not been saved, exit anyway?" );	
		}
		else {
			assert( observable == puzzleHistory );
			if( puzzleHistory.isSaved() || !UserModel.get().isLoggedIn() )
				controller.setConfirmStateChange( null );
			else
				controller.setConfirmStateChange( "Progress has not been saved, exit anyway?" );			
		}
	}

	@Override
	public void enter() {
		if( puzzleModel == null ) {
			controller.backToPreviousState();
			return;
		}
		
		initializePuzzleView();
		enableCallbacks();
		if( type == Type.EDIT_PUZZLE )
			puzzleModel.addObserver( this );
		else if( type == Type.PLAY_PUZZLE || type == Type.CONTINUE_PUZZLE || type == Type.EDIT_TUTORIAL )
			puzzleHistory.addObserver( this );
		controller.setConfirmStateChange( null );
	}

	
	@Override
	public void exit() {
		
		puzzleModel.deleteObserver( this );		
		puzzleHistory.deleteObserver( this );
		disableCallbacks();

		
		Window.enableScrolling(true);
		view.setStandardView();

		extPuzzleView.removeAll();
		
		puzzleModel = null;
		puzzleHistory = null;
		puzzleView = null;
		puzzleController.release();
		puzzleController = null;
	}	
	
	/**
	 * @param model The model to use
	 * @param history The history to use for the puzzle
	 * @param type The type of puzzle this is
	 */
	public void setParams(PuzzleModel model,
			History history, 
			Type type ) {
		this.puzzleModel = model;
		this.puzzleHistory = history;
		this.type  = type;
	}

	private class MyPuzzleCallback implements PuzzleCallback {

		@Override
		public void exit() {
			controller.forwardToPreviousState();
		}
		
		@Override
		public void savePlayHistory( ConnectionLock lock ) {
			Move move = puzzleHistory.saveHistory(lock, puzzleModel.getPuzzleInfoKey(), false);
			puzzleHistory.notifyObservers( move );
		}

		
		@Override
		public void savePuzzle( ConnectionLock lock ) {
			Move move = puzzleModel.savePuzzle(lock);
			puzzleModel.notifyObservers( move );			
		} 
		
		@Override
		public void saveTutorial( ConnectionLock lock ) {
			Move move = puzzleHistory.saveTutorial(lock, puzzleModel );
			puzzleHistory.notifyObservers( move );			
		} 
		
		@Override
		public void checkSolutionAndSave( final ConnectionLock lock ) {	
			boolean match = puzzleModel.checkSolution();
			
			if( match ) {
				moveTimer.stop();
				extPuzzleView.setText( "Puzzle completed in " + moveTimer.getText() );
				extPuzzleView.addTempButton( "Exit", new Executable(){
					@Override
					public void execute() {
						controller.forwardToPreviousState();
					}
				});
				Move move = puzzleHistory.saveHistory(lock, puzzleModel.getPuzzleInfoKey(), true);
				puzzleHistory.notifyObservers( move );
			}
			else
				extPuzzleView.setTimedText( "Puzzle doesn't match solution!", 3000 );
		}
		
	}
	
	/**
	 * Turns all callbacks on
	 */
	private void enableCallbacks() {
		if( puzzleController != null )
			puzzleController.addCallback( callback );
	}

	/**
	 * Turns all defaults callbacks off
	 */
	private void disableCallbacks() {
		puzzleController.removeCallback( callback );
	}


	/**
	 * Create and add puzzle view, only do it once.
	 * Also setup the view and the controller if they are present.
	 */
	private void initializePuzzleView() {
		
		if( extPuzzleView == null ) {
			extPuzzleView = new ExtPuzzleView();
		}

		Window.enableScrolling(false);
		view.setBareView();
		view.setMainWidget(extPuzzleView);

		switch( type ) {
			case PLAY_PUZZLE:
			case VIEW_SOLUTION:
			case VIEW_TUTORIAL:
				moveTimer = new MoveTimerRealtime();
				break;
			case CONTINUE_PUZZLE:
				moveTimer = new MoveTimerRealtime( puzzleHistory.getDuration() );
				break;
			case EDIT_PUZZLE:
				moveTimer = new MoveTimerEmpty();
				break;
			case EDIT_TUTORIAL:
				moveTimer = new MoveTimerRealtime( puzzleHistory.getDuration() );
				moveTimer.setSpeed( 4 );
				break;
		}
			
		
		try {
			
			PuzzleFactory factory = puzzleModel.getFactory(); 
				
			this.puzzleView = factory.createView( puzzleModel );
			puzzleView.setVisible( false );
			extPuzzleView.clearText();
			extPuzzleView.setPuzzleView( puzzleView );
			extPuzzleView.setMoveTimer( moveTimer );
			extPuzzleView.setHistory( puzzleHistory );
			
			if( puzzleController != null )
				puzzleController.release();
			puzzleController = factory.createController( puzzleModel, puzzleHistory, moveTimer, extPuzzleView, controller );
			
		} catch (Exception exception) {
			exception.printStackTrace();
			controller.backToPreviousState();
		}

		switch( type ) {
			case PLAY_PUZZLE:
			case CONTINUE_PUZZLE:
				puzzleController.startPlayPuzzle();
				break;
			case VIEW_SOLUTION:
				puzzleController.startViewSolution();
				break;
			case EDIT_PUZZLE:
				puzzleController.startEditPuzzle();
				break;
			case VIEW_TUTORIAL:
				puzzleController.startViewTutorial();
				break;
			case EDIT_TUTORIAL:
				puzzleController.startEditTutorial();
				break;
		}
		
		view.refreshView();
	}


	@Override
	public void release() {
		puzzleModel.deleteObserver(this);
	}
}
